# coding:utf-8
# 解决execjs编码问题
# import subprocess
# from functools import partial
# subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")
import execjs
# 加载.env配置信息
import os
from dotenv import load_dotenv

load_dotenv()

import re
import cv2
import rsa
import json
import uuid
import time
import hashlib
import subprocess
import shutil
import binascii
import requests
import string
import base64
import random
import ddddocr
from hashlib import *
from PIL import Image
from urllib.parse import urljoin
from urllib.parse import unquote
from Crypto.Util.Padding import pad
from Crypto.Cipher import DES, AES, DES3
from datetime import datetime, timedelta
from utils.log import logger
from Crypto.PublicKey import RSA
from Crypto.Cipher import DES, AES, DES3, PKCS1_v1_5
from config.settings import LOCATION_LIST

WEBHOOK_URL = os.environ.get("WEBHOOK_URL")


def encode_base64(data: str):
    encoded_data = base64.b64encode(data.encode("utf-8"))
    return encoded_data.decode("utf-8")


def decode_base64(encoded_data):
    decoded_data = base64.b64decode(encoded_data).decode("utf-8")
    return decoded_data


def get_sn(num=15):
    """
    sn号 0~9 + a-z 默认15位
    :param num: sn长度 10到15个字符
    :return:
    """

    return "".join(random.sample("123456789" + string.ascii_lowercase, num))


def bsarr_java_topython(byte_list):
    bs = []
    for item in byte_list:
        if item < 0:
            item = item + 256
        bs.append(item)

    return bs


def get_mac_address(sep=':'):
    """
    伪造一波mac地址
    :return:
    """
    data_list = []
    for i in range(6):
        data_list.append(''.join(random.sample(string.hexdigits, 2)))
    return sep.join(data_list).lower()
    # return sep.join(random.sample(string.hexdigits, 12)).lower()


def create_random_mac(sep=":"):
    """ 随机生成mac地址 12位 """
    data_list = []
    for i in range(1, 7):
        part = "".join(random.sample("0123456789ABCDEF", 2))
        data_list.append(part)
    mac = sep.join(data_list)
    return mac


def bytes_python_to_java(bytes):
    """
    参数：字节
    :param bytes:
    :return:
    """
    bs_list = bytearray(bytes)
    return [item - 256 if item >= 128 else item for item in bs_list]


def bytes_java_to_python(bs_list):
    """
    参数：字节
    :param bytes:
    :return:
    """
    return [item + 255 if item < 0 else item for item in bs_list]


def get_now_time():
    """
    获取当前时间
    :return:
    """
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")


def get_current_timestamp10():
    """
    获取10位数时间戳
    :return:
    """
    return "{}".format(int(time.time()))


def aes_encrypt(data_string, key, iv):
    aes = AES.new(
        key=key.encode('utf-8'),
        mode=AES.MODE_CBC,
        iv=iv.encode('utf-8')
    )
    raw = pad(data_string.encode('utf-8'), 16)
    return aes.encrypt(raw)


def get_current_timestamp13():
    """
    获取13位数时间戳
    :return:
    """
    return "{}".format(int(time.time() * 1000))


def my_des(data, key, iv):
    key = key.encode("utf-8")
    iv = iv.encode("utf-8")
    des = DES.new(key=key, iv=iv, mode=DES.MODE_ECB)
    data = pad(data.encode("utf-8"), des.block_size)
    #     加密
    bs = des.encrypt(data)
    #     base64处理
    return base64.b64encode(bs).decode("utf-8")


def get_android(count=16):
    """
    生成安卓id 默认16位
    :param count:
    :return:
    """
    #     31d2a57c0fc9d24a  0-f
    data = string.digits + "abcdef"
    return "".join(random.sample(data, k=count))


def my_aes(data, key, iv):
    key = key.encode("utf-8")
    iv = iv.encode("utf-8")
    aes = AES.new(key=key, iv=iv, mode=AES.MODE_CBC)
    # aes = AES.new(key=key, iv=iv, mode=AES.MODE_ECB)
    data = pad(data.encode("utf-8"), aes.block_size)  # 16
    #     加密
    bs = aes.encrypt(data)
    #     base64处理
    return bs  # b站案例 直接返回字节
    # return base64.b64encode(bs).decode("utf-8")


def str_to_dic(data):
    """
    将请求体 xx=xx%yy=yy -> {xx:xx, yy:yy}
    :param data: 传入的字符串数据
    :return:
    """
    dict_data = {unquote(item.split('=')[0]): unquote(item.split('=')[1]) for item in data.split('&')}
    # 处理urlencode问题
    return json.dumps(dict_data, indent=2,
                      ensure_ascii=False)


def dict_to_str(data_dict):
    """
    字典转urlencode
    :return:
    """
    return '&'.join(["{}={}".format(key, data_dict[key]) for key in sorted(data_dict, reverse=False)])


def non_dict_to_str(data_dict):
    """
    字典转urlencode 没有拼接
    :return:
    """
    return ''.join(["{}={}".format(key, data_dict[key]) for key in data_dict])


def get_uuid():
    return str(uuid.uuid4())


def my_md5(encrypt_data) -> str:
    """
    md5摘要算法加密
    :return:
    """
    obj = md5()
    obj.update(encrypt_data.encode('utf-8'))
    res = obj.hexdigest()
    return res


def tree_map_to_dic(data):
    """
    Java treemap 转为字典
    :return:
    """
    data = data[1:-1]
    dic = json.dumps({
        item.split('=')[0]: item.split('=')[1] for item in data.split(', ')
    }, indent=2, ensure_ascii=False)

    print(dic)


def port_send():
    """
    端口转发
    :return:
    """

    subprocess.getoutput("adb forward tcp:27042 tcp:27042")
    subprocess.getoutput("adb forward tcp:27043 tcp:27043")


def my_3des(data, key, iv):
    """
    车智赢的des3加密
    :param data:
    :param key:
    :param iv:
    :return:
    """
    iv = iv.encode("utf-8")
    # 3des 前24位有效
    key = key[:24].encode("utf-8")

    des = DES3.new(key=key, mode=DES3.MODE_CBC, iv=iv)
    data = pad(data.encode("utf-8"), block_size=des.block_size)
    encrypt_data = des.encrypt(data)
    rs = base64.b64encode(encrypt_data).decode("utf-8")
    return rs


def dic_sort(dic):
    return dict(sorted(dic.items(), key=lambda x: x[0], reverse=False))


def dic_sort_after_tran(dic):
    rs = dic_sort(dic)

    return "".join(["{key}{value}".format(key=k, value=v) for k, v in rs.items()])


def get_android2(count=16):
    data_list = []
    ids = string.digits + "ABCDEF"
    n = len(ids)
    for i in range(16):
        rand_index = random.randint(0, n - 1)
        data = ids[rand_index]
        data_list.append(data)
    return "".join(data_list).lower()


def dic_kv_asc_sort(dic):
    return "".join(["{}{}".format(key, dic[key]) for key in sorted(dic.keys())])


def str_to_bs_list(str_data):
    return [bs for bs in str_data.encode("utf-8")]


def bs_list_to_str(byte_list):
    """
    字节数组 -> 字符串！ 参数也可以是一大堆字节
    :param byte_list:
    :return:
    """
    bs = bytearray(byte_list)
    return bs.decode("utf-8")


def str_to_hex(str_data):
    """
    字符串(字节) 十六进制
    :param str_data:
    :return:
    """

    return ''.join([hex(bs)[2:].rjust(2, '0') for bs in str_data.encode("utf-8")])


def hex_to_str(hex_data):
    """
    十六进制数据 -> 字符串！
    :param hex_data:
    :return:
    """
    bs = binascii.a2b_hex(hex_data)
    return bs.decode("utf-8")


def dict_sorted(dict_data: dict) -> str:
    """
    :param dict_data:
    :return:
    """
    result = "&".join(["{}={}".format(key, dict_data[key]) for key in sorted(dict_data.keys())])
    return result


def get_sort_urlencode(dic_data):
    """
    字典排序 -> urlencode
    :param dic_data:
    :return:
    """
    return "&".join(["{}={}".format(key, dic_data[key]) for key in sorted(dic_data.keys())])


def unpack_(filename, extract_dir):
    """
    解压操作
    :param filename:
    :param extract_dir:
    :return:
    """
    shutil.unpack_archive(r"C:\Users\Administrator\Desktop\招聘项目报告.docx.zip", format="zip",
                          extract_dir=r"C:\Users\Administrator\Desktop\videos")


def get_current_timestamp():
    """
    当前时间戳
    :return:
    """
    return str(int(time.time() * 1000))


def create_android_id():
    data_list = []
    for i in range(1, 9):
        part = "".join(random.sample("0123456789ABCDEF", 2))
        data_list.append(part)
    return "".join(data_list).lower()


def dumps(data_dict):
    """
    字典数据序列化
    :return:
    """
    return json.dumps(data_dict, separators=(',', ':'))


def parse_cookie(cookie_str):
    """
    cookie格式化
    :param cookie_str:
    :return:
    """
    cookie_dict = {}
    items = cookie_str.split('; ')
    for item in items:
        key, value = item.split('=')
        cookie_dict[key] = value
    return cookie_dict


def my_sha384(data: str):
    obj = hashlib.sha384()
    obj.update(data.encode("utf-8"))
    return obj.hexdigest()


def my_sha256(data: str, salt=None):
    obj = hashlib.sha256()
    obj.update(data.encode("utf-8"))
    if salt:
        obj.update(salt.encode("utf-8"))
    return obj.hexdigest()


def delete_blank(str_data: str):
    """
    删除空白
    :param str_data:被处理的字符串数据
    :return:
    """
    return str_data.replace('\n', '').replace('\r', '').replace(' ', '')


def get_yesterday():
    # 计算昨天的日期
    # 1
    yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
    return yesterday


def get_now_date():
    """
    返回当前日期
    """
    # 获取当前日期 时分秒
    current_date = datetime.now().strftime("%Y-%m-%d")
    return current_date


def get_previous_date(n: int):
    """
    获取当前天 往前移n天
    """
    current_date = datetime.now()
    for i in range(n):
        previous_date = current_date - timedelta(days=i)
        formatted_date = previous_date.strftime("%Y-%m-%d")
        print(formatted_date)


def send_feishu_message(message):
    """
    推送至飞书API
    :param message:
    :return:
    """
    while True:
        try:
            resp = requests.post(WEBHOOK_URL, headers={
                'Content-Type': 'application/json'
            }, data=json.dumps({
                'msg_type': 'text',
                'content': {
                    'text': message
                }
            }))
            if resp.status_code == 200:
                logger.debug("飞书推送成功!".format(resp.text))
                return
            else:
                logger.warning("飞书推送、状态码不对! error: {}".format(resp.text))
                time.sleep(3)

        except Exception as e:
            logger.warning("飞书消息推送失败! error: {}".format(e))
            time.sleep(3)


def get_rand_bs_39(num):
    """
    py3.9版本随机字节
    :return:
    """
    return "".join([hex(item)[2:] for item in random.randbytes(num)])


def get_rand_bs(num):
    """
    随机生成4个字节，然后转成16进制
    字节范围 0~256
    :param num:
    :return:
    """
    bytes_list = [random.randint(0, 255) for _ in range(num)]
    bytes_obj = bytearray(bytes_list)
    bs = ''.join([hex(b)[2:].rjust(2, '0') for b in bytes_obj])
    # bs2= binascii.b2a_hex(bytes_obj)
    return bs


# 滑块相关


def trun_back(path):
    """
    pip install pillow
    还原图片
    """
    old_img = Image.open(path)
    # 创建一张新图
    new_img = Image.new("RGB", (260, 160))
    Ut = [
        39,
        38,
        48,
        49,
        41,
        40,
        46,
        47,
        35,
        34,
        50,
        51,
        33,
        32,
        28,
        29,
        27,
        26,
        36,
        37,
        31,
        30,
        44,
        45,
        43,
        42,
        12,
        13,
        23,
        22,
        14,
        15,
        21,
        20,
        8,
        9,
        25,
        24,
        6,
        7,
        3,
        2,
        0,
        1,
        11,
        10,
        4,
        5,
        19,
        18,
        16,
        17
    ]
    r = 160
    a = r // 2
    for _ in range(52):
        c = Ut[_] % 26 * 12 + 1
        if 25 < Ut[_]:
            u = a
        else:
            u = 0
        # 获取一个区域, (x1, y1, x2, y2)
        l = old_img.crop((c, u, c + 10, u + a))

        x1 = _ % 26 * 10
        if 25 < _:
            y1 = a
        else:
            y1 = 0
        new_img.paste(l, (x1, y1))
    new_img.save(f"new_{path}")


def jsonp_handle(text):
    jsonp_re = re.compile(r"\((?P<code>.*)\)", re.S)
    jsonp_str = jsonp_re.search(text, re.S).group("code")
    return json.loads(jsonp_str)


def download_img(name, url, session):
    resp = session.get(url)
    file_path = os.path.join(os.getcwd(), "images", name)
    with open(file_path, mode="wb") as f:
        f.write(resp.content)


def get_distance(target_bytes, background_bytes):
    """
    计算滑块距离 (滑块/背景)
    :return: 左上角 右下脚 坐标
    """
    slide = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
    res = slide.slide_match(target_bytes, background_bytes, simple_target=True)
    distance = res["target"][0]
    return distance


def ocr_slide_distance(target_bytes, background_bytes):
    """
    云片滑块 计算滑块距离
    :return:
    """
    slide = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
    res = slide.slide_match(target_bytes, background_bytes)
    # print(res)
    return res["target"][0]


def my_rsa(publick_key: str, data: str):
    """
    rsa加密
    :return:
    """
    pub_key = RSA.importKey(base64.b64decode(publick_key))
    rsa = PKCS1_v1_5.new(pub_key)
    result = base64.b64encode(rsa.encrypt(data.encode("utf-8"))).decode()
    return result


def get_x():
    """
    pip install python-opencv
    pip install opencv-python
    import cv2  # 导入cv2之后没有代码提示. 怎么办?
    # 计算滑块滑动的距离
    # opencv来完成计算
    """

    # 读取两张图
    bg = cv2.imread(os.path.join(os.getcwd(), "images", "new_bg.jpg"))
    slice = cv2.imread(os.path.join(os.getcwd(), "images", "slice.jpg"))

    # 做灰度处理
    bg = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY)
    slice = cv2.cvtColor(slice, cv2.COLOR_BGR2GRAY)

    # 图片边缘处理
    bg_can = cv2.Canny(bg, 255, 255)
    slice = cv2.Canny(slice, 255, 255)

    # 匹配图像的相似度, TM_CCOEFF_NORMED参数固定即可
    r = cv2.matchTemplate(bg_can, slice, cv2.TM_CCOEFF_NORMED)

    # 获取匹配度最好的一个结果
    minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(r)

    x = maxLoc[0]
    # y = maxLoc[1]
    return x


def __ease_out_expo(sep):
    """
    极验三代的极验轨迹
    """
    if sep == 1:
        return 1
    else:
        return 1 - pow(2, -10 * sep)


def get_slide_track4(distance):
    """
    根据滑动距离生成滑动轨迹 四代轨迹
    :param distance: 需要滑动的距离
    :return: 滑动轨迹<type 'list'>: [[x,y,t], ...]
        x: 已滑动的横向距离
        y: 已滑动的纵向距离, 除起点外, 均为0
        t: 滑动过程消耗的时间, 单位: 毫秒
    """
    ttt = 0
    if not isinstance(distance, int) or distance < 0:
        raise ValueError(f"distance类型必须是大于等于0的整数: distance: {distance}, type: {type(distance)}")
    # 初始化轨迹列表
    slide_track = [
        [random.randint(20, 60), random.randint(10, 40), 0]
    ]
    # 共记录count次滑块位置信息
    count = 30 + int(distance / 2)
    # 初始化滑动时间
    t = random.randint(50, 100)
    # 记录上一次滑动的距离
    _x = 0
    _y = 0
    for i in range(count):
        # 已滑动的横向距离
        x = round(__ease_out_expo(i / count) * distance)
        # 滑动过程消耗的时间
        t = random.randint(10, 20)
        if x == _x:
            continue
        slide_track.append([x - _x, _y, t])
        _x = x
        ttt += t
    slide_track.append([0, 0, random.randint(200, 300)])
    return slide_track, ttt

def get_slide_track(distance):
    import random
    """
    根据滑动距离生成滑动轨迹 3代轨迹
    :param distance: 需要滑动的距离
    :return: 滑动轨迹<type 'list'>: [[x,y,t], ...]
        x: 已滑动的横向距离
        y: 已滑动的纵向距离, 除起点外, 均为0
        t: 滑动过程消耗的时间, 单位: 毫秒
    """

    if not isinstance(distance, int) or distance < 0:
        raise ValueError(f"distance类型必须是大于等于0的整数: distance: {distance}, type: {type(distance)}")
    # 初始化轨迹列表
    slide_track = [
        [random.randint(-50, -10), random.randint(-50, -10), 0],
        [0, 0, 0],
    ]
    # 共记录count次滑块位置信息
    count = 30 + int(distance / 2)
    # 初始化滑动时间
    t = random.randint(50, 100)
    # 记录上一次滑动的距离
    _x = 0
    _y = 0
    for i in range(count):
        # 已滑动的横向距离
        x = round(__ease_out_expo(i / count) * distance)
        # 滑动过程消耗的时间
        t += random.randint(10, 20)
        if x == _x:
            continue
        slide_track.append([x, _y, t])
        _x = x
    slide_track.append([distance, 0, t])

    return slide_track, t


def get_restore(picture):
    """
    图片还原
    :param picture:
    :return:
    """
    file_path = os.path.join(os.getcwd(), "images", picture)
    im = Image.open(file_path)
    im_list_upper = []
    im_list_down = []
    for location in LOCATION_LIST:
        if location['y'] == -58:
            im_list_upper.append(
                im.crop((abs(location['x']), 58, abs(location['x']) + 10, 116)))  # 有关参数的介绍请看下方的crop方法的介绍， 可自行调整
        if location['y'] == 0:
            im_list_down.append(im.crop((abs(location['x']), 0, abs(location['x']) + 10, 0 + 58)))

    new_im = Image.new('RGB', (260, 116))

    x_offset = 0
    for im in im_list_upper:
        new_im.paste(im, (x_offset, 0))
        x_offset += im.size[0]

    x_offset = 0
    for im in im_list_down:
        new_im.paste(im, (x_offset, 58))
        x_offset += im.size[0]
    new_file_path = os.path.join(os.getcwd(), "images", f"new_{picture}")
    new_im.save(new_file_path)


def rsa_tow_num(p, q, aeskey):
    """
    rsa算法 两个数字
    :param p: 第一次数字 十六进制
    :param q: 第二次数字 十六进制
    :param aeskey: rsa公钥
    :return:
    """
    key = rsa.PublicKey(int(p, 16), int(q, 16))
    rsa_result = rsa.encrypt(aeskey.encode(), key)
    return rsa_result.hex()


if __name__ == '__main__':
    get_rand_bs(4)
